/* -------------------------------------------------------------------------
 *
 * pgstat_slru.c
 *	  SLRU统计的实现。
 *
 * 本文件包含SLRU统计的实现。它与pgstat.c保持分开，以便区分统计访问/
 * 存储实现与个别统计类型的详细信息。
 *
 * Copyright (c) 2001-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	  src/backend/utils/activity/pgstat_slru.c
 * -------------------------------------------------------------------------
 */

#include "postgres.h"

#include "utils/pgstat_internal.h"
#include "utils/timestamp.h"


static inline PgStat_SLRUStats *fc_get_slru_entry(int fc_slru_idx);
static void fc_pgstat_reset_slru_counter_internal(int fc_index, TimestampTz fc_ts);


/*
 * SLRU统计计数等待被刷新。我们假设这个变量
 * 初始化为零。条目与slru_names[]一一对应。SLRU计数器的
 * 变化在关键段内报告，因此我们使用静态内存
 * 以避免内存分配。
 */
static PgStat_SLRUStats pending_SLRUStats[SLRU_NUM_ELEMENTS];
bool		have_slrustats = false;


/*
 * 重置单个SLRU的计数器。
 *
 * 对此函数的权限检查通过正常的
 * GRANT系统管理。
 */
void pgstat_reset_slru(const char *fc_name)
{
	TimestampTz fc_ts = GetCurrentTimestamp();

	AssertArg(fc_name != NULL);

	fc_pgstat_reset_slru_counter_internal(pgstat_get_slru_index(fc_name), fc_ts);
}

/*
 * SLRU统计计数累积函数---从slru.c调用
 */

void pgstat_count_slru_page_zeroed(int fc_slru_idx)
{
	fc_get_slru_entry(fc_slru_idx)->blocks_zeroed += 1;
}

void pgstat_count_slru_page_hit(int fc_slru_idx)
{
	fc_get_slru_entry(fc_slru_idx)->blocks_hit += 1;
}

void pgstat_count_slru_page_exists(int fc_slru_idx)
{
	fc_get_slru_entry(fc_slru_idx)->blocks_exists += 1;
}

void pgstat_count_slru_page_read(int fc_slru_idx)
{
	fc_get_slru_entry(fc_slru_idx)->blocks_read += 1;
}

void pgstat_count_slru_page_written(int fc_slru_idx)
{
	fc_get_slru_entry(fc_slru_idx)->blocks_written += 1;
}

void pgstat_count_slru_flush(int fc_slru_idx)
{
	fc_get_slru_entry(fc_slru_idx)->flush += 1;
}

void pgstat_count_slru_truncate(int fc_slru_idx)
{
	fc_get_slru_entry(fc_slru_idx)->truncate += 1;
}

/*
 * SQL可调用pgstat*函数的支持函数。返回
 * 指向slru统计结构的指针。
 */
PgStat_SLRUStats *
pgstat_fetch_slru(void)
{
	pgstat_snapshot_fixed(PGSTAT_KIND_SLRU);

	return pgStatLocal.snapshot.slru;
}

/*
 * 返回索引的SLRU名称。索引可能超出SLRU_NUM_ELEMENTS，
 * 在这种情况下返回NULL。这允许编写不
 * 事先知道条目数量的代码。
 */
const char * pgstat_get_slru_name(int fc_slru_idx)
{
	if (fc_slru_idx < 0 || fc_slru_idx >= SLRU_NUM_ELEMENTS)
		return NULL;

	return slru_names[fc_slru_idx];
}

/*
 * 确定具有给定名称的SLRU的条目索引。如果没有精确
 * 匹配，则返回用于外部项目定义的
 * SLRU的最后一个“其他”条目的索引。
 */
int pgstat_get_slru_index(const char *fc_name)
{
	int			fc_i;

	for (fc_i = 0; fc_i < SLRU_NUM_ELEMENTS; fc_i++)
	{
		if (strcmp(slru_names[fc_i], fc_name) == 0)
			return fc_i;
	}

	/* 返回最后一个条目的索引（即“其他”条目） */
	return (SLRU_NUM_ELEMENTS - 1);
}

/*
 * 刷新本地待处理的SLRU统计条目
 *
 * 如果nowait为真，则在锁定失败时此函数返回false。
 * 否则，此函数始终返回true。
 *
 * 如果nowait为真，则如果无法获得锁，则此函数返回true。
 * 否则返回false。
 */
bool pgstat_slru_flush(bool fc_nowait)
{
	PgStatShared_SLRU *fc_stats_shmem = &pgStatLocal.shmem->slru;
	int			fc_i;

	if (!have_slrustats)
		return false;

	if (!fc_nowait)
		LWLockAcquire(&fc_stats_shmem->lock, LW_EXCLUSIVE);
	else if (!LWLockConditionalAcquire(&fc_stats_shmem->lock, LW_EXCLUSIVE))
		return true;

	for (fc_i = 0; fc_i < SLRU_NUM_ELEMENTS; fc_i++)
	{
		PgStat_SLRUStats *fc_sharedent = &fc_stats_shmem->stats[fc_i];
		PgStat_SLRUStats *fc_pendingent = &pending_SLRUStats[fc_i];

#define SLRU_ACC(fld) fc_sharedent->fld += fc_pendingent->fld
		SLRU_ACC(blocks_zeroed);
		SLRU_ACC(blocks_hit);
		SLRU_ACC(blocks_read);
		SLRU_ACC(blocks_written);
		SLRU_ACC(blocks_exists);
		SLRU_ACC(flush);
		SLRU_ACC(truncate);
#undef SLRU_ACC
	}

	/* 完成，清除待处理条目 */
	MemSet(pending_SLRUStats, 0, sizeof(pending_SLRUStats));

	LWLockRelease(&fc_stats_shmem->lock);

	have_slrustats = false;

	return false;
}

void pgstat_slru_reset_all_cb(TimestampTz fc_ts)
{
	for (int fc_i = 0; fc_i < SLRU_NUM_ELEMENTS; fc_i++)
		fc_pgstat_reset_slru_counter_internal(fc_i, fc_ts);
}

void pgstat_slru_snapshot_cb(void)
{
	PgStatShared_SLRU *fc_stats_shmem = &pgStatLocal.shmem->slru;

	LWLockAcquire(&fc_stats_shmem->lock, LW_SHARED);

	memcpy(pgStatLocal.snapshot.slru, &fc_stats_shmem->stats,
		   sizeof(fc_stats_shmem->stats));

	LWLockRelease(&fc_stats_shmem->lock);
}

/*
 * 返回指向具有给定SLRU计数器的条目的指针（基于存储在
 * SlruCtl中的lwlock tranche名称）。
 */
static inline PgStat_SLRUStats *
fc_get_slru_entry(int fc_slru_idx)
{
	pgstat_assert_is_up();

	/*
	 * 后台进程永远不应该注册任何SLRU统计计数；如果这样做，
	 * 计数将通过fork()复制到子进程中。
	 */
	Assert(IsUnderPostmaster || !IsPostmasterEnvironment);

	Assert((fc_slru_idx >= 0) && (fc_slru_idx < SLRU_NUM_ELEMENTS));

	have_slrustats = true;

	return &pending_SLRUStats[fc_slru_idx];
}

static void fc_pgstat_reset_slru_counter_internal(int fc_index, TimestampTz fc_ts)
{
	PgStatShared_SLRU *fc_stats_shmem = &pgStatLocal.shmem->slru;

	LWLockAcquire(&fc_stats_shmem->lock, LW_EXCLUSIVE);

	memset(&fc_stats_shmem->stats[fc_index], 0, sizeof(PgStat_SLRUStats));
	fc_stats_shmem->stats[fc_index].stat_reset_timestamp = fc_ts;

	LWLockRelease(&fc_stats_shmem->lock);
}
